package com.atguigu.tingshu.payment.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.payment.PaymentInfo;
import com.atguigu.tingshu.order.client.OrderInfoFeignClient;
import com.atguigu.tingshu.payment.config.WxPayV3Config;
import com.atguigu.tingshu.payment.service.PaymentInfoService;
import com.atguigu.tingshu.payment.service.WxPayService;
import com.atguigu.tingshu.payment.util.PayUtil;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.exception.ValidationException;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class WxPayServiceImpl implements WxPayService {

    @Autowired
    private PaymentInfoService paymentInfoService;

    @Autowired
    private WxPayV3Config wxPayV3Config;

    @Autowired
    private RSAAutoCertificateConfig config;

    @Autowired
    private UserInfoFeignClient userInfoFeignClient;

    @Autowired
    private OrderInfoFeignClient orderInfoFeignClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public void wxNotify(HttpServletRequest request) {
		//	微信支付平台证书的序列号
		String wechatPaySerial = request.getHeader("Wechatpay-Serial");
		//	签名中的随机数
		String wechatpayNonce = request.getHeader("Wechatpay-Nonce");
		//	签名中的时间戳
		String wechatTimestamp = request.getHeader("Wechatpay-Timestamp");
		//	应答的微信支付签名
		String wechatSignature = request.getHeader("Wechatpay-Signature");
		//	获取原始报文
		String requestBody = PayUtil.readData(request);
		// 构造 RequestParam
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(wechatPaySerial)
                .nonce(wechatpayNonce)
                .signature(wechatSignature)
                .timestamp(wechatTimestamp)
                .body(requestBody)
                .build();
		// 初始化 NotificationParser
        NotificationParser parser = new NotificationParser(config);
		//	获取数据
        try {
            // 以支付通知回调为例，验签、解密并转换成 Transaction
            Transaction transaction = parser.parse(requestParam, Transaction.class);
            //  判断是否成功.
            if (transaction.getTradeState().equals(Transaction.TradeStateEnum.SUCCESS)) {
                //  加锁：setnx key value;
                try {
                    Boolean result = this.redisTemplate.opsForValue().setIfAbsent(transaction.getOutTradeNo(), transaction.getOutTradeNo(), 24 * 60 + 4, TimeUnit.MINUTES);
                    if (!result){
                        throw new GuiguException(ResultCodeEnum.REPEAT_CALLBACK);
                    }
                    //  打印信息
                    System.out.println(JSON.toJSONString(transaction));
                    //  更新交易记录与订单记录.
                    paymentInfoService.updatePaymentStatus(transaction);
                } catch (GuiguException e) {
                    //  如果消费失败，就删除key，让后续的消息进来。
                    this.redisTemplate.delete(transaction.getOutTradeNo());
                    throw new RuntimeException(e);
                }
            } else {
                //    没有校验成功.
                System.out.println("校验失败.");
            }

        } catch (ValidationException e) {
            // 签名验证失败，返回 401 UNAUTHORIZED 状态码
            log.error("sign verification failed", e);
			throw new GuiguException(ResultCodeEnum.UNAUTHORIZED);
        }
    }

    @Override
    public Transaction queryPayStatus(String orderNo) {
        //	创建服务对象
        JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();
        //	根据微信订单号
        //	QueryOrderByIdRequest queryRequest = new QueryOrderByIdRequest();
        //	根据商户的订单号
        QueryOrderByOutTradeNoRequest queryRequest = new QueryOrderByOutTradeNoRequest();
        //	直连商户的商户号
        queryRequest.setMchid(wxPayV3Config.merchantId);
        //	商户系统内部订单号
        queryRequest.setOutTradeNo(orderNo);

        try {
            //	查询数据
            Transaction result = service.queryOrderByOutTradeNo(queryRequest);
            System.out.println(result.getTradeState());
            //	返回数据
            return result;
        } catch (ServiceException e) {
            // API返回失败, 例如ORDER_NOT_EXISTS
            System.out.printf("code=[%s], message=[%s]\n", e.getErrorCode(), e.getErrorMessage());
            System.out.printf("reponse body=[%s]\n", e.getResponseBody());
        }
        return null;
    }

    @Override
    public Map<String, Object> createJsapi(String paymentType, String orderNo, Long userId) {
        //	细节处理：
        //	判断业务类型以及保存支付交易记录.
        if (!SystemConstant.PAYMENT_TYPE_RECHARGE.equals(paymentType)) {
            //	根据订单Id 获取到订单对象
            Result<OrderInfo> orderInfoResult = orderInfoFeignClient.getOrderInfo(orderNo);
            Assert.notNull(orderInfoResult, "订单结果集为空");
            OrderInfo orderInfo = orderInfoResult.getData();
            Assert.notNull(orderInfo, "订单为空");
            //	查看当前订单状态.
            //	判断订单的状态
            if (SystemConstant.ORDER_STATUS_CANCEL.equals(orderInfo.getOrderStatus())) {
                //	直接返回
                return new HashMap<>();
            }
        }
        //	保存交易记录
        PaymentInfo paymentInfo = paymentInfoService.savePaymentInfo(paymentType, orderNo, userId);
        //	config
        //	创建服务对象
        JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();
        //	创建支付请求对象
        PrepayRequest request = new PrepayRequest();
        Amount amount = new Amount();
        //	金钱单位：分 支付宝：元
        amount.setTotal(1); // 订单的总金额;
        request.setAmount(amount);
        request.setAppid(wxPayV3Config.getAppid());
        request.setMchid(wxPayV3Config.getMerchantId());
        request.setDescription("测试商品标题");
        request.setNotifyUrl(wxPayV3Config.getNotifyUrl());
        request.setOutTradeNo(orderNo);
        //	下订单的时间+10m; 15:45 下单; 15:55; cancel:5m  15m
        //	request.setTimeExpire(); 给的绝对时间 yyyy-MM-dd HH:mm:ss

        //	创建一个对象
        Payer payer = new Payer();
        //	获取wxOpenId
        Result<UserInfoVo> userInfoVoResult = userInfoFeignClient.getUserInfoVo(userId);
        Assert.notNull(userInfoVoResult, "用户结果集为空");
        UserInfoVo userInfoVo = userInfoVoResult.getData();
        Assert.notNull(userInfoVo, "用户为空");
        payer.setOpenid(userInfoVo.getWxOpenId());
        request.setPayer(payer);

        // response包含了调起支付所需的所有参数，可直接用于前端调起支付
        PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);
        Map<String, Object> result = new HashMap<>();
        result.put("timeStamp", response.getTimeStamp());
        result.put("nonceStr", response.getNonceStr());
        result.put("package", response.getPackageVal());
        result.put("signType", response.getSignType());
        result.put("paySign", response.getPaySign());
        //	微信支付：
        return result;
    }
}

